/* Copyright (c) 2000 James Archibald, Brigham Young University */
/* Modifications copyright (c) 2006-2007 David A. Penry, Brigham Young University */
/* Lab completed by James Carroll, Fall 2008 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifndef max
#define max(x,y) ((x)>(y)?(x):(y))
#endif

#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (1)
#endif

#include "stalldef.h"	/* #defines in countstall parameters */
#include "ops.h"

/* these specify the latencies used: will remain constant throughout
 all labs.  */
#define MAXUNITS 5
#define LATENCY_INT  1
#define LATENCY_ADD  2
#define LATENCY_LOAD 2
#define LATENCY_MUL  5
#define LATENCY_DIV 19

#define NUMBER_OF_ARCH_REGS (97)

/********* global variables declared in another file that you will need at
 some point to complete the labs *************/
extern int verbose; /* flag: set if verbose output expected */
extern int in_execution; /* flag: set if currently executing */
extern int i_count; /* number of instructions executed */
extern int nop_count; /* number of nops executed */
extern char verbosestr[]; /* string output in verbose mode */
extern int branch_flag; /* branch outcome flag */
extern int b_count; /* number of branch instructions so far */
extern int j_count; /* number of jump instructions so far */
extern int pc; /* program counter */
extern unsigned long ir; /* instruction register */
extern int newpc; /* BTA for taken branches */
extern unsigned int ldstaddr; /* ref address for loads&stores */

/******************************* stamp buffers ********************************/

/* structure to hold buffer timestamp records in increasing order */
typedef struct ts_s {
	unsigned long stamp;
	struct ts_s *next; /* in newer direction */
	struct ts_s *prev; /* in older direction */
} ts_t;
ts_t *tsFree = NULL; /* buffer timestamp free list */

typedef struct {
	int occupancy; /* how many in buffer or list */
	ts_t *oldest; /* oldest is the head of the list */
} tsBuffer_t;

static void remove_all(tsBuffer_t *buf) {
	if (buf->occupancy) {
		buf->oldest->prev->next = tsFree;
		tsFree = buf->oldest;
	}
	buf->occupancy = 0;
	buf->oldest = NULL;
}

static void remove_older_stamps(tsBuffer_t *buf, unsigned long stamp) {
	while (buf->occupancy && buf->oldest->stamp <= stamp) {
		ts_t *p = buf->oldest->next;
		buf->oldest->next = tsFree;
		tsFree = buf->oldest;
		buf->oldest = p;
		buf->occupancy--;
		if (buf->occupancy) {
			p->prev = tsFree->prev;
			tsFree->prev->next = p;
		}
	}
}

static ts_t *get_new_stamp() {
	if (tsFree) {
		ts_t *p = tsFree;
		tsFree = tsFree->next;
		return p;
	} else
		return (ts_t *) malloc(sizeof(ts_t));
}

static void insert_stamp(tsBuffer_t *buf, unsigned long stamp) {
	ts_t *q;
	q = get_new_stamp();
	q->stamp = stamp;
	if (!buf->occupancy) {
		q->prev = q;
		q->next = q;
		buf->oldest = q;
	} else {
		ts_t *p;
		if (stamp < buf->oldest->stamp) { /* new head */
			p = buf->oldest->prev; /* insert after tail but rotate to become head */
			buf->oldest = q;
		} else {
			p = buf->oldest->prev; /* move back from tail */
			while (stamp < p->stamp)
				p = p->prev;
			/* Post: stamp >= p->stamp */
		}
		q->prev = p;
		q->next = p->next;
		p->next = q;
		q->next->prev = q;
	}
	buf->occupancy++;
}

static int count_stamp(tsBuffer_t *buf, unsigned long stamp) {
	int count = 0;
	ts_t *p;
	if (!buf->occupancy)
		return 0;
	p = buf->oldest;
	do {
		if (p->stamp == stamp)
			count++;
		p = p->next;
	} while (p != buf->oldest);
	return count;
}

/****************************************************************************/

/********** globals that specify the simulated configuration ***********/

int latency[5]; /* latencies for all f_units, initialized with
 #defines above */
int ibufSize; /* instruction buffer size */
int iwSize; /* instruction window size */
int robSize; /* size of the reorder buffer, zero if no ROB */
int prfSize; /* extra physical registers for renaming */
int stbufSize; /* store buffer size */
int issueOOO; /* issue out of order */

#define NOBP      0
#define PERFECTBP 1
#define STATICBP  2
#define DYNAMICBP 3
int bpType; /* branch prediction type */

/******************* globals to track state of pipeline *****************/
unsigned long prevIFcycle;
unsigned long prevIDcycle;
unsigned long prevIScycle;
unsigned long prevCTcycle;

unsigned long branchRedirect; /* cycle in which redirect takes effect */

/* global variables to store resource timestamps; denotes last cycle before
 * placed into bypass network/CDB for last write to architectural register
 */
unsigned long IRstamps[32]; /* int regs */
unsigned long FRstamps[32]; /* float regs */
unsigned long DRstamps[32]; /* double regs */
unsigned long FPSstamps; /* FP status bit updates */

/* global variables to store identity of last funit to write */
unsigned long IRwriter[32]; /* int regs */
unsigned long FRwriter[32]; /* float regs */
unsigned long DRwriter[32]; /* double regs */
unsigned long FPSwriter; /* FP status bit */

tsBuffer_t iBuf = { 0, NULL}; /* instruction buffer */
tsBuffer_t iWindow = { 0, NULL}; /* instruction window */
tsBuffer_t stBuf = { 0, NULL}; /* store buffer */
tsBuffer_t roBuf = { 0, NULL}; /* reorder buffer */
tsBuffer_t prFile = { 0, NULL}; /* physical register file */
tsBuffer_t pendingStores = { 0, NULL}; /* stores that haven't issued yet */
tsBuffer_t issueBuffer = { 0, NULL}; /* ensure one instruction issues per cycle */
int issueWidth = 1;

/*
 * TODO: DEFINE ADDITIONAL DATA STRUCTURES HERE
 */

/****** global variables defined here for statistics of interest ***********/

#define RAWindex 0
#define WAWindex 1
#define Sindex 2
unsigned long totalstalls; /* total stall count */
unsigned long specstalls[MAXUNITS+1][3]; /* specific stall counts, see tpressed
 for interpretation */
unsigned long totalcycles; /* total cycles required to execute */

unsigned long branchstalls; /* total unfetched slots due to branches */
unsigned long unfetched; /* total unfetched slots due to branches */
unsigned long robFullstalls; /* dispatch slots lost because ROB full */
unsigned long noPhysRegstalls; /* dispatch slots lost because no more
 physical registers
 */
unsigned long stbufFullstalls; /* dispatch slots lost because of full
 store buffer
 */
unsigned long iwFullstalls; /* dispatch slots lost because IW full */
unsigned long issuewidthstalls; /* issue width reached */
unsigned long stldstalls; /* ld/st conflict stalls */
unsigned long dispatchunused; /* stalls at dispatch */

/* handy global used to simplify output routine */
char *f_unit_name[6] = { "int", "load", "add", "mult", "div", "PRF" };

/******************* Initialization and options ************************/

void init_latency(void) {
	latency[INT_UNIT] = LATENCY_INT;
	latency[ADD_UNIT] = LATENCY_ADD;
	latency[LOAD_UNIT] = LATENCY_LOAD;
	latency[MUL_UNIT] = LATENCY_MUL;
	latency[DIV_UNIT] = LATENCY_DIV;
	issueOOO = TRUE;
	prfSize = 128; /* 31 extra registers */
	robSize = 64;
	iwSize = 32;
	stbufSize = 16;
	ibufSize = 1;
	bpType = NOBP;
}

int handle_option(char *arg) {
	if (!strncmp(arg, "--lat:int=", 10)) {
		latency[INT_UNIT] = atoi(arg + 10);
	} else if (!strncmp(arg, "--lat:add=", 10)) {
		latency[ADD_UNIT] = atoi(arg + 10);
	} else if (!strncmp(arg, "--lat:load=", 11)) {
		latency[LOAD_UNIT] = atoi(arg + 11);
	} else if (!strncmp(arg, "--lat:mul=", 10)) {
		latency[MUL_UNIT] = atoi(arg + 10);
	} else if (!strncmp(arg, "--lat:div=", 10)) {
		latency[DIV_UNIT] = atoi(arg + 10);
	} else if (!strncmp(arg, "--size:ibuf=", 12)) {
		ibufSize = atoi(arg + 12);
	} else if (!strncmp(arg, "--size:stbuf=", 13)) {
		stbufSize = atoi(arg + 13);
	} else if (!strncmp(arg, "--size:iw=", 10)) {
		iwSize = atoi(arg + 10);
	} else if (!strncmp(arg, "--size:rob=", 11)) {
		robSize = atoi(arg + 11);
	} else if (!strncmp(arg, "--size:prf=", 11)) {
		prfSize = atoi(arg + 11);
		if (prfSize) {
			if (prfSize <= NUMBER_OF_ARCH_REGS) {
				fprintf(
						stderr,"There must be more than %d physical registers\n",
						NUMBER_OF_ARCH_REGS);
				exit(1);
			}
		}
	} else if (!strcmp(arg, "--issueOOO=yes")) {
		issueOOO = TRUE;
	} else if (!strcmp(arg, "--issueOOO=no")) {
		issueOOO = FALSE;
	} else if (!strcmp(arg, "--bp=none")) {
		bpType = NOBP;
	} else if (!strcmp(arg, "--bp=perfect")) {
		bpType = PERFECTBP;
	} else
		return 0;
	return 1;
}

void print_options(FILE *fp) {
	fprintf(fp, "--lat:<unit>=<#>     Set latency of unit to #\n");
	fprintf(fp, "                        Units are int,load,add,mul,div\n");
	fprintf(fp, "--size:ibuf=<#>      Size of instruction buffer\n");
	fprintf(fp, "--size:iw=<#>        Instruction window size\n");
	fprintf(fp, "--size:rob=<#>       Reorder buffer size\n");
	fprintf(fp, "--size:prf=<#>       Physical register file size\n");
	fprintf(fp, "--size:stbuf=<#>     Store buffer size\n");
	fprintf(fp, "--issueOOO=[yes|no]            Issue out of order\n");
	fprintf(fp, " --bp=<type>          Branch prediction: none or perfect\n");
}

void clearstall(void) {
	/* This function is called at the beginning of execution and it
	 initializes all data structures used to track timing. */
	ts_t *p, *q;

	int i;
	for (i = 0; i < 32; i++) {
		IRstamps[i] = 0;
		FRstamps[i] = 0;
		DRstamps[i] = 0;
		IRwriter[i] = 0;
		FRwriter[i] = 0;
		DRwriter[i] = 0;
	}
	FPSstamps = 0;
	FPSwriter = 0;

	prevIFcycle = 0;
	prevIDcycle = 1;
	prevIScycle = 2;
	prevCTcycle = 0;

	/* clean up buffers */
	remove_all(&iBuf);
	remove_all(&iWindow);
	remove_all(&stBuf);
	remove_all(&roBuf);
	remove_all(&prFile);

	/*
	 * TODO: INITIALIZE OR ALLOCATE DATA STRUCTURES HERE
	 */

	/* clean up stats */
	totalstalls = 0;
	totalcycles = 0;
	stldstalls = 0;
	for (i = 0; i < MAXUNITS; i++) {
		specstalls[i][0] = 0;
		specstalls[i][1] = 0;
	}
	robFullstalls = 0;
	iwFullstalls = 0;
	noPhysRegstalls = 0;
	stbufFullstalls = 0;
	dispatchunused = 0;
}

int mylog2(n)
	int n; {
	int cnt = 0;
	while ((n & 0x1) != 1) {
		cnt++;
		n = n >> 1;
	}
	return (cnt);
}

unsigned long opavail(int optype, int rnum) {
	switch (optype) {
	case NONE:
		return 0;
	case INTR:
		return IRstamps[rnum];
	case FPR:
		return FRstamps[rnum];
	case DPR:
		return DRstamps[rnum];
	case FPS:
		return FPSstamps;
	default:
		fprintf(stderr,"Bad value in opwb() \n");
		return 0;
	}
}

int opwriter(int optype, int rnum) {
	switch (optype) {
	case NONE:
		return 0;
	case INTR:
		return IRwriter[rnum];
	case FPR:
		return FRwriter[rnum];
	case DPR:
		return DRwriter[rnum];
	case FPS:
		return FPSwriter;
	default:
		fprintf(stderr,"Bad value in opwriter() \n");
		return 0;
	}
}

void addstalls(int s1type, int src1, int s2type, int src2, int funit,
		int rtype, int result) {
	/*   s1type, s2type, rtype:
	 NONE (0) => no operand
	 INTR (1) => int register
	 FPR  (2) => float register (32 bits)
	 DPR  (3) => double register (64 bits)
	 FPS  (4) => fpstatus
	 src1, src2, result:
	 specifies register number
	 funit:  the functional unit required by this instruction
	 (one of #defines in stalldef.h)
	 INT_UNIT
	 LOAD_UNIT
	 ADD_UNIT
	 MUL_UNIT
	 DIV_UNIT
	 NOP_UNIT (don't execute this instruction)
	 */

	/* Here is the algorithm for the basic pipeline:

	 1. Check for RAW and WAW hazards: determine latest time that a)
	 source operands will be available, and b) that most recent
	 write to dest register will be complete to avoid WAW hazard.
	 (These are done by comparing the current time with each
	 register's timestamp.)  If a RAW and WAW hazard exist on
	 the same register, ignore the WAW hazard.

	 If one or more hazards cause a stall, attribute entire stall to
	 functional unit of latest hazard to clear.  In case of tie,
	 attribute to src1 if involved, else src2.  (There isn't a
	 terrific reason for this other than that we can match results
	 exactly.)

	 2. Having determined the issue cycle of the current
	 instruction, update all timing variables to reflect the
	 appropriate passage of time.

	 3. Update the time stamp for the destination register (if any)
	 with the last cycle on which the new value is NOT available */

	int i, max1, maxunit1, max2, maxunit2, max3, maxunit3, stalls;
	unsigned long BGcycle, IFcycle, IDcycle, IScycle, EXcycle, WBcycle,
			WPcycle, CRcycle, CTcycle;
	unsigned long cycleTmp, diff;
	char stallstr[500], tmpbuf[200];

	/* clear output strings if they will be used */
	if (verbose || in_execution) {
		verbosestr[0] = '\0';
		stallstr[0] = '\0';
	}

	/************************ Fetch ****************************/

	BGcycle = prevIFcycle; /* will be IF of last instruction */

	/* when do we fetch this?
	 * 1) we fetch in the same cycle until we reach maximum fetch
	 *    for the cycle OR
	 * 2) we fill up the instruction buffer
	 */

	/* First possible fetch cycle */
	IFcycle = prevIFcycle + 1;
	remove_older_stamps(&iBuf, IFcycle);

	/* if we have to stall for a full Ibuf, do it here by finding the first
	 * ID cycle in which something is removed from the buffer
	 */
	while (iBuf.occupancy >= ibufSize) {
		IFcycle = iBuf.oldest->stamp;
		remove_older_stamps(&iBuf, IFcycle);
	}

	/* And we're done figuring out fetch */
	prevIFcycle = IFcycle;

	/******************** Dispatch ****************************/

	/* Check for structural hazards on dispatch.  For
	 the unified windows, it's checking of dispatch width.  For the
	 rest it's checking of issue width.  For many there will also
	 be checks of various structure sizes.  The order in which I check is:
	 1) Dispatch/issue width
	 2) ensure we're after previous fetch!
	 3) ROB size
	 4) IW size
	 5) Store buffer size
	 6) Lack of rename registers

	 Any stalls here delay ID.
	 */

	/* first possible IDcycle -- checked here because it's not a stall
	 * if you have to go over...
	 */

	IDcycle = prevIDcycle + 1;

	/* if fetch was delayed, then we don't get to go yet!.  Only has an
	 * effect if there's a branch predictor in place.
	 */
	if (IDcycle <= IFcycle) {
		stalls = IFcycle - IDcycle + 1;
		unfetched += stalls;
		dispatchunused += stalls;
		IDcycle = IFcycle + 1;
	}

	// Check for full reorder buffer (ROB)
	cycleTmp = IDcycle;
	remove_older_stamps(&roBuf, IDcycle);
	while (roBuf.occupancy >= robSize) {
		IDcycle = roBuf.oldest->stamp;
		remove_older_stamps(&roBuf, IDcycle);
	}
	robFullstalls += cycleTmp - IDcycle;

	// Check for full instruction window (iWindow)
	cycleTmp = IDcycle;
	if (funit != NOP && funit != TRAP) {
		remove_older_stamps(&iWindow, IDcycle);
		while (iWindow.occupancy >= iwSize) {
			IDcycle = iWindow.oldest->stamp;
			remove_older_stamps(&iWindow, IDcycle);
		}
	}
	iwFullstalls += cycleTmp - IDcycle;

	// Check for full store buffer
	cycleTmp = IDcycle;
	if (funit == LOAD_UNIT && s2type != NONE) {
		remove_older_stamps(&stBuf, IDcycle);
		while (stBuf.occupancy >= stbufSize) {
			IDcycle = stBuf.oldest->stamp;
			remove_older_stamps(&stBuf, IDcycle);
		}
	}
	stbufFullstalls += cycleTmp - IDcycle;

	// Check for full physical register file
	cycleTmp = IDcycle;
	if (rtype != NONE) {
		remove_older_stamps(&prFile, IDcycle);
		while (prFile.occupancy >= prfSize) {
			IDcycle = prFile.oldest->stamp;
			remove_older_stamps(&prFile, IDcycle);
		}
	}
	noPhysRegstalls += cycleTmp - IDcycle;


	/*
	 * TODO: additional dispatch stage checks which delay IDcycle
	 *       and increment individual stall counters and dispatchunused.
	 */

	prevIDcycle = IDcycle;

	/*************************** ISSUE ******************************/

	// earliest possible IS cycle
	IScycle = IDcycle + 1;

	/* NOW... figure out when we're going to actually issue.  The hard
	 part about this is that any cycle we choose could be bad because:
	 1) We have a RAW hazard.  If we have no SRF and are unable
	 to bypass from the ROB, then we cannot go in many conditions!
	 2) We have a WAW hazard; only affects in-order issue
	 3) Load after store checks
	 4) already issued too many instructions in chosen cycle
	 */

	/* 1. check for RAW hazards */
	max1 = opavail(s1type, src1); /* check first operand */
	if (max1 <= IScycle)
		max1 = 0;
	else
		maxunit1 = opwriter(s1type, src1);

	max2 = opavail(s2type, src2); /* check second operand */
	if (max2 && funit == LOAD_UNIT && s2type != NONE)
		max2--; /* store */
	if (max2 <= IScycle)
		max2 = 0;
	else
		maxunit2 = opwriter(s2type, src2);

	if (max1 > 0 || max2 > 0) {
		if (max2 > max1) {
			max1 = max2;
			maxunit1 = maxunit2;
		}

		stalls = max1 - IScycle;
		specstalls[maxunit1][RAWindex] += stalls;
		totalstalls += stalls;
		IScycle = max1;
		if (verbose || in_execution) { /* update stall string */
			sprintf(tmpbuf, " RAW:(%d, %s)", stalls, f_unit_name[maxunit1]);
			strcat(stallstr, tmpbuf);
		}
	}

	/* 2. check for WAW hazards, but only if destination register != src regs*/
	// if ((s1type != rtype || src1 != result) &&
	//	(s2type != rtype || src2 != result)) {
	//
	//      max3 = opavail(rtype, result);
	//      if (max3 > IScycle) maxunit3 = opwriter(rtype, result);
	//      else max3 = 0;
	//    }
	//    else max3 = 0;
	//
	//    if (max3 > 0) {
	//      stalls = max3 - IScycle;
	//      specstalls[maxunit1][WAWindex] += stalls;
	//      totalstalls += stalls;
	//      IScycle = max3;
	//      if (verbose || in_execution) { /* update stall string */
	//	sprintf(tmpbuf, " WAW:(%d, %s)", stalls, f_unit_name[maxunit1]);
	//	strcat(stallstr, tmpbuf);
	//      }
	//    }

	/*
	 * TODO: additional issue checks, updating IScycle, individual stall
	 *       counts, and totalstalls
	 */

	// Loads cannot issue until all previous stores have issued
	if (funit == LOAD_UNIT && rtype != NONE) {
		unsigned long newestStoreIssueCycle = pendingStores.oldest->prev->stamp;
		if (newestStoreIssueCycle >= IScycle) {
			stldstalls += newestStoreIssueCycle - IScycle;
			totalstalls += newestStoreIssueCycle - IScycle;
			IScycle = newestStoreIssueCycle;
		}
	}

	// ensure only one instruction issues per cycle
	if (funit != NOP_UNIT && funit != TRAP) {
		cycleTmp = IScycle;
		while (count_stamp(&issueBuffer, cycleTmp) >= issueWidth)
			cycleTmp++;
		issuewidthstalls += cycleTmp - IScycle;
		totalstalls += cycleTmp - IScycle;
		IScycle = cycleTmp;
	}

	insert_stamp(&issueBuffer, IScycle);
	remove_older_stamps(&issueBuffer, IFcycle);

	prevIScycle = IScycle;

	/* TODO: remove the following two lines for OoO behavior */
	//    IDcycle = IScycle - 1;  // stall ID cycle
	//    prevIDcycle = IDcycle;

	/********************* Execution & WB *************************/

	if (funit == NOP_UNIT)
		EXcycle = IScycle + 1; /* nops + traps*/
	else {
		EXcycle = IScycle + latency[funit];
	}
	WBcycle = EXcycle + 1;

	/* now ensure we commit in-order.  This is done by making
	 * sure that we commit no earlier than the last instruction.  We
	 * set WPcycle to the point where the previous one committed.
	 */
	WPcycle = max(WBcycle+1,prevCTcycle) - 1;

	/* Now deal with commit bandwidth limits */
	CRcycle = WPcycle;
	CTcycle = max(CRcycle,prevCTcycle) + 1;

	prevCTcycle = CTcycle;

	totalcycles = max(IScycle+1,totalcycles);

	/* 3. update result register timestamps */
	switch (rtype) {
	case NONE:
		break;
	case INTR:
		IRstamps[result] = EXcycle;
		IRwriter[result] = funit;
		break;
	case FPR:
		FRstamps[result] = EXcycle;
		FRwriter[result] = funit;
		break;
	case DPR:
		DRstamps[result] = EXcycle;
		DRwriter[result] = funit;
		break;
	case FPS:
		FPSstamps = EXcycle;
		FPSwriter = funit;
		break;
	}

	// Record the cycle on which we left each buffer
	insert_stamp(&iBuf, IDcycle);
	insert_stamp(&roBuf, CTcycle);
	if (funit == LOAD_UNIT && s2type != NONE)
		insert_stamp(&stBuf, CTcycle);
	if (rtype != NONE)
		insert_stamp(&prFile, CTcycle);
	if (funit != NOP && funit != TRAP)
		insert_stamp(&iWindow, CTcycle);
	if (funit == LOAD_UNIT && s2type != NONE) {
		insert_stamp(&pendingStores, EXcycle - 1);
		remove_older_stamps(&pendingStores, EXcycle - 2);
	}

	/*
	 * TODO: any additional data structure manipulations
	 */

	if (branchRedirect) /* in delay slot of mispred */
	{
		if (branchRedirect > IDcycle) { /* nominal time */
			stalls = branchRedirect - IDcycle;
			if (verbose || in_execution) {
				sprintf(tmpbuf, " BP:(%d)", stalls);
				strcat(stallstr, tmpbuf);
			}
			branchstalls += stalls;
			totalstalls += stalls;
			prevIFcycle = branchRedirect - 1;
		} else {
			/* make sure we don't say we fetched it ridiculously early when
			 * instruction in delay slot stalled
			 */
			prevIFcycle = max(branchRedirect-1,prevIFcycle);
			if (verbose || in_execution) {
				sprintf(tmpbuf, " BP:(0)");
				strcat(stallstr, tmpbuf);
			}
		}
		branchRedirect = 0;
	}
	{
		int brlat;
		/* take a look at branches */
		/* For this lab, assume no predictor and computation in EX */
		if ((brlat = ((branch_flag == NOTABRANCH || bpType == PERFECTBP) ? 0
				: WBcycle - IDcycle))) {
			branchRedirect = IDcycle + brlat; /* time when new IF should occur */
		} else {
			branchRedirect = 0;
		}
	}

	/* generate verbose output if desired */
	if (verbose || in_execution) {
		sprintf(verbosestr, "[%5lu IF+%2lu ID+%2lu IS=%5lu "
			"EX+%2lu WB=%5lu wp+%2lu cr+%2lu CT=%5lu]%s", BGcycle, IFcycle
				- BGcycle, IDcycle - IFcycle, IScycle, EXcycle - IScycle,
				WBcycle, WPcycle - WBcycle, CRcycle - WPcycle, CTcycle,
				stallstr);
	}
}

void tpressed(void) {
	/* This function is called when the user presses the 't' key at
	 the mydlx> prompt.  It prints timing information, including
	 classifications of stall types to give you some idea what you
	 should speed up to get better performance.  As the pipeline
	 changes, the possible causes of stalls also change, so this
	 routine and the data structures it accesses need to be
	 updated. */

	if (totalcycles == 0) {
		printf("  No instructions executed\n");
		return;
	}
	printf("  Stall cycles\n");
	printf("    integer:  RAW        %10u  [%2d%%]\n",
			specstalls[INT_UNIT][RAWindex],
			(int) (specstalls[INT_UNIT][RAWindex] / (totalcycles / 100.0)));
	printf("              WAW        %10u  [%2d%%]\n",
			specstalls[INT_UNIT][WAWindex],
			(int) (specstalls[INT_UNIT][WAWindex] / (totalcycles / 100.0)));
	printf("    ld/store: RAW        %10u  [%2d%%]\n",
			specstalls[LOAD_UNIT][RAWindex],
			(int) (specstalls[LOAD_UNIT][RAWindex] / (totalcycles / 100.0)));
	printf("              WAW        %10u  [%2d%%]\n",
			specstalls[LOAD_UNIT][WAWindex],
			(int) (specstalls[LOAD_UNIT][WAWindex] / (totalcycles / 100.0)));
	printf("    FP add:   RAW        %10u  [%2d%%]\n",
			specstalls[ADD_UNIT][RAWindex],
			(int) (specstalls[ADD_UNIT][RAWindex] / (totalcycles / 100.0)));
	printf("              WAW        %10u  [%2d%%]\n",
			specstalls[ADD_UNIT][WAWindex],
			(int) (specstalls[ADD_UNIT][WAWindex] / (totalcycles / 100.0)));
	printf("    FP mult:  RAW        %10u  [%2d%%]\n",
			specstalls[MUL_UNIT][RAWindex],
			(int) (specstalls[MUL_UNIT][RAWindex] / (totalcycles / 100.0)));
	printf("              WAW        %10u  [%2d%%]\n",
			specstalls[MUL_UNIT][WAWindex],
			(int) (specstalls[MUL_UNIT][WAWindex] / (totalcycles / 100.0)));
	printf("    FP div:   RAW        %10u  [%2d%%]\n",
			specstalls[DIV_UNIT][RAWindex],
			(int) (specstalls[DIV_UNIT][RAWindex] / (totalcycles / 100.0)));
	printf("              WAW        %10u  [%2d%%]\n",
			specstalls[DIV_UNIT][WAWindex],
			(int) (specstalls[DIV_UNIT][WAWindex] / (totalcycles / 100.0)));
	printf("    issue width limited: %10u  [%2d%%]\n", issuewidthstalls,
			(int) (issuewidthstalls / (totalcycles / 100.0)));
	printf("    st/ld conflicts:     %10u  [%2d%%]\n", stldstalls,
			(int) (stldstalls / (totalcycles / 100.0)));

	printf("    branch  :            %10u  [%2d%%]\n", branchstalls,
			(int) (branchstalls / (totalcycles / 100.0)));
	printf("  ROB full:              %10u  [%2d%%]\n", robFullstalls,
			(int) (robFullstalls / (totalcycles / 100.0)));
	printf("  IW full:               %10u  [%2d%%]\n", iwFullstalls,
			(int) (iwFullstalls / (totalcycles / 100.0)));
	printf("  Out of PRs:            %10u  [%2d%%]\n", noPhysRegstalls,
			(int) (noPhysRegstalls / (totalcycles / 100.0)));
	printf("  Store buffer full:     %10u  [%2d%%]\n", stbufFullstalls,
			(int) (stbufFullstalls / (totalcycles / 100.0)));
	printf("  Unfetched :            %10u  [%2d%%]\n", unfetched,
			(int) (unfetched / (totalcycles / 100.0)));
	printf("  Total dispatch unused: %10u  [%2d%%]\n", dispatchunused,
			(int) (dispatchunused / (totalcycles / 100.0)));
	printf("  Total other stalls:    %10u  [%2d%%]\n", totalstalls,
			(int) (totalstalls / (totalcycles / 100.0)));
	printf("  Total nops:            %10u  [%2d%%]\n", nop_count,
			(int) (nop_count / (totalcycles / 100.0)));
	printf("  Useful instr:          %10u \n", i_count - nop_count);
	printf("  Total cycles:          %10u \n", totalcycles);
	printf("  CPI:                   %10.2f\n", (double) totalcycles / (i_count
			- nop_count));

}

static char *bpredtypename[] = { "none", "perfect", "static", "dynamic" };

void ppressed(void) {
	/* This function is called when the user presses the 'p' key at
	 the mydlx> prompt.  It prints the current CPU configuration.
	 As simulated configurations increase in complexity, it should
	 be extended to output all relevant CPU parameters.  */

	printf("--Basic pipeline configuration--\n");
	printf("      UNIT     LATENCY\n");
	printf("  Integer unit:  %2d\n", latency[INT_UNIT]);
	printf("  Load unit:     %2d\n", latency[LOAD_UNIT]);
	printf("  FP adder:      %2d\n", latency[ADD_UNIT]);
	printf("     multiplier: %2d\n", latency[MUL_UNIT]);
	printf("     divider:    %2d\n", latency[DIV_UNIT]);

	printf("Instruction buffer size:      %2d\n", ibufSize);
	printf("Out-of-order issue:          %3s\n", issueOOO ? "yes" : "no");
	printf("ROB size:                    %3d\n", robSize);
	printf("Store buffer size:           %3d\n", stbufSize);
	printf("Instruction window size:     %3d\n", iwSize);
	printf("Physical register file size: %3d\n", prfSize);
}

void fatalerrormsg(void) {
	/* This function is called when any sort of run-time error is
	 detected and execution is terminated.  It simply prints out the
	 cycle count (maintained only in stall.c code) to help the user
	 determine what happened and when.  */
	fprintf(stderr,"Current cycle: %lu\n", totalcycles);
}

void zpressed(void) {
	/* This function is called when the user presses the 'z' key at
	 the prompt.  It will be used in a later lab to dump branch
	 prediction info and can be ignored until then.  It can be used
	 in the meantime to print anything you want (for debugging
	 purposes) whenever 'z' is pressed. */
	printf("Function not implemented.\n");
}
